package com.competition.web.admin.log.impl;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Invocation;
import com.competition.web.admin.service.ILogContentService;
import com.competition.web.admin.service.ILogService;
import com.competition.web.admin.service.ITableColumnInfoService;
import com.basic.service.contract.IBaseService;
import com.competition.web.admin.util.SpringBeanUtil;
import com.competition.web.entity.Log;
import com.competition.web.entity.LogContentWithBLOBs;
import com.competition.web.admin.log.IOperatorLog;

/**
 * 
 * @author 111
 *
 */
public class UpdateLog extends AbstractLog implements IOperatorLog {

	/**
	 * if (param instanceof MapperMethod.ParamMap) { // //
	 * MapperMethod.ParamMap<Object> paramMap = (ParamMap<Object>) param; // //
	 * String columnString = sql.substring(sql.indexOf("set"),
	 * sql.indexOf("where")); // // String[] columns =
	 * columnString.trim().split(","); // // List<ParameterMapping>
	 * parameterMappings = boundSql.getParameterMappings(); // // Object
	 * parameterObject = boundSql.getParameterObject(); // // TypeHandlerRegistry
	 * typeHandlerRegistry =
	 * mappedStatement.getConfiguration().getTypeHandlerRegistry(); // //
	 * Configuration configuration = mappedStatement.getConfiguration(); // //
	 * ParameterMapping parameterMapping; // for (int i = 0; i <
	 * parameterMappings.size(); i++) { // // parameterMapping =
	 * parameterMappings.get(i); // // if (parameterMapping.getMode() ==
	 * ParameterMode.OUT) { // continue; // } // // Object value; // String
	 * propertyName = parameterMapping.getProperty(); // if
	 * (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for
	 * additional params // value = boundSql.getAdditionalParameter(propertyName);
	 * // } else if (parameterObject == null) { // value = null; // } else if
	 * (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { // value =
	 * parameterObject; // } else { // MetaObject metaObject =
	 * configuration.newMetaObject(parameterObject); // value =
	 * metaObject.getValue(propertyName); // } // TypeHandler<?> typeHandler =
	 * parameterMapping.getTypeHandler(); // JdbcType jdbcType =
	 * parameterMapping.getJdbcType(); // if (value == null && jdbcType == null) {
	 * // jdbcType = configuration.getJdbcTypeForNull(); // } // try { // //
	 * content.setCurrentValue(value.toString()); // // content.setValue(tableName);
	 * // // content.setKey(columns[i]); // // typeHandler.setParameter(ps, i + 1,
	 * value, jdbcType); // } catch (TypeException | SQLException e) { // throw new
	 * TypeException( // "Could not set parameters for mapping: " + parameterMapping
	 * + ". Cause: " + e, e); // } // } // // } // // }
	 */

	/**
	 * // Object idValue; // // Field idField = target.getDeclaredField("id"); // //
	 * idValue = this.getPropertyValue(idField.getName(), target, param); // //
	 * String idString = idValue.toString(); // // Object object =
	 * baseService.selectByPrimaryKey(Integer.valueOf(idString)); // // Map<String,
	 * Object> mapOrigialData = getDataToMap(object); // // Map<String, Object>
	 * newData = getDataToMap(param); // // cleanNoNeedData(mapOrigialData); // //
	 * cleanNoNeedData(newData); // // mapOrigialData =
	 * removeTheSame(mapOrigialData, newData); // // if (0 == mapOrigialData.size())
	 * { // 未作修改 // return 1; // } // // ILogService logService =
	 * SpringBeanUtil.getBean(ILogService.class); // // Log log = new Log(); // //
	 * log.setTableId(Long.valueOf(idString)); // // int status =
	 * logService.insertParse(log, tableName, (byte) 1); // // if (0 == status) { //
	 * 添加日志失败 // return 1; // } // // Object origialData; // // Map<String, String>
	 * tableColumn = SpringBeanUtil.getBean(ITableColumnInfoService.class) //
	 * .getTableComment(tableName); // // long time = System.currentTimeMillis() /
	 * 1000; // // for (Map.Entry<String, Object> item : mapOrigialData.entrySet())
	 * { // name = item.getKey(); // // if (name.equals("id")) { // continue; // }
	 * // // value = this.getPropertyValue(name, target, param); // // if (null ==
	 * value) { // value = ""; // } // // content = new LogContentWithBLOBs(); // //
	 * content.setKey(name); // // content.setCreateTime(time); // ////
	 * System.out.println("name: " + name); // // origialData =
	 * mapOrigialData.get(name); // // if (null == origialData) { // origialData =
	 * ""; // } // // content.setValue(origialData.toString()); // //
	 * content.setComment(tableColumn.get(name)); // //
	 * content.setCurrentValue(value.toString()); // //
	 * content.setLogId(log.getId()); // // content.setId(0L); // //
	 * logContents.add(content); // } // // ILogContentService logContentService =
	 * SpringBeanUtil.getBean(ILogContentService.class); // //
	 * logContentService.batchInsert(logContents.stream().toArray(LogContentWithBLOBs[]::new));
	 * 
	 */
	@Override
	public Integer callBack(Object param, BoundSql boundSql, MappedStatement mappedStatement, Invocation invocation) {
		/**
		 * // 排除 update mg_competition_info set status = ? , a = ? where id in ( ? );
		 */

		String tableName = this.getTableName(boundSql);

		if (this.recordLog(tableName)) {
			return 1;
		}

		if (param instanceof ParamMap) {

			return this.parseParamter((ParamMap) param, boundSql, tableName);
		}

		String serviceName = this.getServiceName(tableName);

		try {
			IBaseService<Object> baseService = SpringBeanUtil.getBean(serviceName);

			Class<?> target = param.getClass();

			try {

				Map<String, String> tableColumn = SpringBeanUtil.getBean(ITableColumnInfoService.class)
						.getTableComment(tableName);
				;

				return addLog(param, target, baseService, tableName, tableColumn);

			} catch (Exception e) {
				e.printStackTrace();
			}

			return 1;
		} catch (Exception e) {
			// TODO
			e.printStackTrace();
			return 1;
		}
	}

	protected String getTableName(BoundSql boundSql) {
		String sql = boundSql.getSql().toLowerCase();
		// update a set
		String updateTableSet = sql.substring(0, sql.indexOf("set"));

		return updateTableSet.replace("update", "").replace("\n", "").replace("\t", "").trim();
	}

	protected <V> int addLog(V entity, Class<?> target, IBaseService<Object> baseService, String tableName,
			Map<String, String> tableColumn) throws NoSuchMethodException, SecurityException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {

		List<LogContentWithBLOBs> logContents = new ArrayList<>();

		LogContentWithBLOBs content;

		String name;

		Object value;

		int id = this.getId(entity, entity.getClass());

		Object object = baseService.selectByPrimaryKey(id);
		
		if (null == object) {
			return 1;
		}
		

		Map<String, Object> mapOrigialData = getDataToMap(object);

		Map<String, Object> newData = getDataToMap(entity);

		cleanNoNeedData(mapOrigialData);

		newData = cleanNull(newData);

		cleanNoNeedData(newData);

		ILogService logService = SpringBeanUtil.getBean(ILogService.class);

		Log log = new Log();

		log.setTableId(Long.valueOf(id));

		int status = logService.insertParse(log, tableName, (byte) 1);

		if (0 == status) { // 添加日志失败
			return 1;
		}

		Object origialData;

		long time = System.currentTimeMillis() / 1000;

		for (Map.Entry<String, Object> item : newData.entrySet()) {
			name = item.getKey();

			if (name.equals("id")) {
				continue;
			}

			value = this.getPropertyValue(name, target, entity);

			if (null == value) {
				continue;
			}

			content = new LogContentWithBLOBs();

			content.setKey(name);

			content.setCreateTime(time);

//			System.out.println("name: " + name);

			origialData = mapOrigialData.get(name);

			if (null == origialData) {
				origialData = "";
			}

			content.setValue(origialData.toString());

			content.setComment(tableColumn.get(name));

			content.setCurrentValue(value.toString());

			content.setLogId(log.getId());

			content.setId(0L);

			logContents.add(content);
		}

		ILogContentService logContentService = SpringBeanUtil.getBean(ILogContentService.class);

		return logContentService.batchInsert(logContents.stream().toArray(LogContentWithBLOBs[]::new));
	}

	/**
	 * 处理带参数的[update mg_competition_info set status = ? , a = ? where id in ( ?
	 * );][updateStatusBatchByData]
	 * 
	 * @param <V>
	 * @param <V>
	 * @param paramMap
	 */
	protected <V> int parseParamter(ParamMap<V[]> paramMap, BoundSql boundSql, String tableName) {

		V[] data = paramMap.get("datas");

		String serviceName = this.getServiceName(data[0].getClass());

		IBaseService<Object> baseService = SpringBeanUtil.getBean(serviceName);

		Map<String, String> tableColumn = SpringBeanUtil.getBean(ITableColumnInfoService.class)
				.getTableComment(tableName);

		Class<?> target;
		V entity;
		try {
			for (int i = 0; i < data.length; i++) {

				entity = data[0];

				target = entity.getClass();

				this.addLog(entity, target, baseService, tableName, tableColumn);

			}
		} catch (Exception e) {
			throw new Error(e);
		}

		return 1;
	}
}